import java.util.LinkedList;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.Semaphore;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 我们提供了一个类：
 *
 * public class Foo {
 *   public void first() { print("first"); }
 *   public void second() { print("second"); }
 *   public void third() { print("third"); }
 * }
 * 三个不同的线程将会共用一个 Foo 实例。
 *
 * 线程 A 将会调用 first() 方法
 * 线程 B 将会调用 second() 方法
 * 线程 C 将会调用 third() 方法
 * 请设计修改程序，以确保 second() 方法在 first() 方法之后被执行，third() 方法在 second() 方法之后被执行。
 *
 * 来源：力扣（LeetCode）
 * 链接：https://leetcode-cn.com/problems/print-in-order
 * 著作权归领扣网络所有。商业转载请联系官方授权，非商业转载请注明出处。
 */
public class A按序打印 {

    /**
     * 把并发调用变成按顺序调用
     */
    private static class 方案一CountDownLatch实现{
        //维护两个CountDownLatch c1,c2，初始状态都是1，是锁住的状态
        //c1是用来协作first跟second方法的，如果first需要通知second的时候只要取减一，同时将second从等待队列里面唤醒去执行second的方法
        //c2是用来协作second跟third方法的（同上）
        CountDownLatch stage1 = new CountDownLatch(1);
        CountDownLatch stage2 = new CountDownLatch(1);
        public void first(Runnable printFirst) throws InterruptedException {
            printFirst.run();
            stage1.countDown();
        }
        public void second(Runnable printSecond) throws InterruptedException {
            stage1.await();
            printSecond.run();
            stage2.countDown();
        }
        public void third(Runnable printThird) throws InterruptedException {
            stage2.await();
            printThird.run();
        }
        public static void main(String[] args) throws InterruptedException {
            方案一CountDownLatch实现 a=new 方案一CountDownLatch实现();
            Thread thread2=new Thread(()-> {
                try {
                    a.third(()-> System.out.println("3"));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
            thread2.start();

            Thread thread=new Thread(()-> {
                try {
                    a.second(()-> System.out.println("2"));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
            thread.start();

            Thread thread1=new Thread(()-> {
                try {
                    a.first(()-> System.out.println("1"));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
            thread1.start();
        }
    }
    private static class 方案二Semaphore实现{
        Semaphore stage1 = new Semaphore(0);
        Semaphore stage2 = new Semaphore(0);
        public void first(Runnable printFirst)  {
            printFirst.run();
            stage1.release();
        }
        public void second(Runnable printSecond)  {
            try {
                stage1.acquire();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            printSecond.run();
            stage2.release();
        }
        public void third(Runnable printThird)  {
            try {
                stage2.acquire();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            printThird.run();
        }
        public static void main(String[] args) throws InterruptedException {
            方案二Semaphore实现 a=new 方案二Semaphore实现();
            new Thread(()->a.third(()-> System.out.println("3"))).start();
            new Thread(()->a.second(()-> System.out.println("2"))).start();
            new Thread(()->a.first(()-> System.out.println("1"))).start();
        }
    }
    private static class 方案三Lock{
        Lock lock = new ReentrantLock();
        volatile int stage = 1;
        Condition stage2 = lock.newCondition();
        Condition stage3 = lock.newCondition();

        public void first(Runnable printFirst)   {
            try {
                lock.lock();
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                printFirst.run();
                stage = 2;
                stage2.signal();
            }finally {
                System.out.println("stage:"+stage);

                lock.unlock();
            }
        }
        public void second(Runnable printSecond){
            try {
                lock.lock();
                while(stage!=2) {

                    ReentrantLock reentrantLock =new ReentrantLock();


                    try {
                        stage2.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                printSecond.run();
                stage = 3;
                stage3.signal();
            }finally {
                lock.unlock();
                System.out.println("stage:"+stage);
            }
        }

        public void third(Runnable printThird) {
            try {
                lock.lock();
                while(stage!=3) {
                    try {
                        stage3.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                printThird.run();
            }finally {
                System.out.println("stage:"+stage);
                lock.unlock();
            }
        }
        public static void main(String[] args){
            throw new RuntimeException("1");
        }
    }
    private static class 方案四无锁{
        volatile int stage = 1;
        public void first(Runnable printFirst){
            printFirst.run();
            stage = 2;
        }
        public void second(Runnable printSecond){
            while(stage!=2) {
            }
            printSecond.run();
            stage = 3;
        }

        public void third(Runnable printThird){
            while(stage!=3) {
            }
            printThird.run();
        }

        public static void main(String[] args) {
            方案四无锁 a=new 方案四无锁();
            new Thread(()->a.third(()-> System.out.println("3"))).start();
            new Thread(()->a.second(()-> System.out.println("2"))).start();
            new Thread(()->a.first(()-> System.out.println("1"))).start();
        }
    }
    private static class 方案五CyclicBarrier{
        CyclicBarrier cb1=new CyclicBarrier(2);
        CyclicBarrier cb2=new CyclicBarrier(2);
        public void first(Runnable printFirst){
            printFirst.run();
            try {
                cb1.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            }
        }
        public void second(Runnable printSecond){
            try {
                cb1.await();
                printSecond.run();
                cb2.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            }
        }

        public void third(Runnable printThird){
            try {
                cb2.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            }
            printThird.run();
        }
        public static void main(String[] args) {
            方案五CyclicBarrier a=new 方案五CyclicBarrier();
            new Thread(()->a.third(()-> System.out.println("3"))).start();
            new Thread(()->a.second(()-> System.out.println("2"))).start();
            new Thread(()->a.first(()-> System.out.println("1"))).start();
        }
    }
}

class Test{
    public  static void test3() {
        synchronized(Test.class){
            try {
                Thread.sleep(2000L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("3");
        }
    }
    public synchronized static void test4()  {
        try {
            Thread.sleep(1000L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("4");
    }
    public synchronized static void test5()  {
        try {
            Thread.sleep(1000L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("5");
    }
    public static void main(String[] args) throws InterruptedException {
        long start=System.currentTimeMillis()/1000;
        new Thread(()-> test4()).start();
        Thread.sleep(1000L);
        new Thread(()-> test5()).start();
        long end=System.currentTimeMillis()/1000;
        System.out.println(end-start);
        //Test t=new Test();
        //t.test1();
        //t.test2();
        //new Thread(new Runnable() {
        //    @Override
        //    public void run() {
        //        try {
        //            t.test1();
        //        } catch (InterruptedException e) {
        //            e.printStackTrace();
        //        }
        //    }
        //}).start();
        //new Thread(new Runnable() {
        //    @Override
        //    public void run() {
        //        t.test2();
        //    }
        //}).start();
    }

    private class Node{
        int val;
        int frequency;
        public void setVal(int val){
            this.val=val;
        }
        public void setFrequency(int fre){
            this.frequency=fre;
        }
        public int getVal(){
            return this.frequency;
        }
        public int getFre(){
            return this.frequency;
        }
    }
}


class Test12{
    LinkedList<Object> linkedList =new LinkedList();
    ReentrantLock reentrantLock = new ReentrantLock();
    Condition isEmpty =reentrantLock.newCondition();
    Condition isFull =reentrantLock.newCondition();

    public void add(Object integer){
        reentrantLock.lock();
        if(linkedList.isEmpty()){

        }
    }



}